#include "GameObject.h"
#include "BoonException.h"
#include "ProcessManager.h"
#include "Messenger.h"
#include "event/Event_GameObject_Created.h"
#include "component/Transform.h"
#include "component/Renderer.h"
#include "component/Rigidbody.h"
#include <string>


using namespace Boon;


Boon::GameObject::CreationCBPerCompName* Boon::GameObject::m_CreationCallbacks = nullptr;
GameObject::MapGameObjects Boon::GameObject::m_GameObjects;
Boon::GameObject::MapGameObjects Boon::GameObject::m_NewCreated;

void Boon::GameObject::RegisterComponentCreationCallback( const std::string& id, ComponentCreationCallback cb )
{
	if( !m_CreationCallbacks )
		m_CreationCallbacks = new CreationCBPerCompName();
	(*m_CreationCallbacks)[id] = cb;
}


Boon::GameObject::GameObject( const char* name )
	:	Object( name ),
		m_Transform( nullptr ),
		m_Renderer( nullptr ),
		m_Collider( nullptr ),
		m_Rigidbody( nullptr ),
		m_Parent( nullptr )
{

}

Boon::GameObject::GameObject( const char* name, const uint_32 componentCount )
	:	Object( name ),
		m_Transform( nullptr ),
		m_Renderer( nullptr ),
		m_Collider( nullptr ),
		m_Rigidbody( nullptr ),
		m_Parent( nullptr )
{

}

Boon::GameObject::~GameObject()
{
	// calling OnDisable for all enabled components
	auto iter = m_UpdatedComponents.begin();
	auto end = m_UpdatedComponents.end();
	for( ; iter != end; ++iter )
		iter->OnDisable();

	// clear all enabled components
	m_UpdatedComponents.clear();

	// invoke on destroy
	auto idestroy = m_ComponentsPerCompID.begin();
	auto endDestroy = m_ComponentsPerCompID.end();
	for( ; idestroy != endDestroy; ++idestroy )
		idestroy->second->OnDestroy();

	// Finally delete them ( GameObject is the only holder of the Strong-Reference )
	m_ComponentsPerCompID.clear();
}

GameObjectWeak Boon::GameObject::Create( const char* name )
{
	// create gameobject
	GameObjectStrong ref( new GameObject( name ) );
	m_NewCreated[ref->GetUniqueObjectID()] = ref;

	// register on processmanager
	Event_GameObject_CreatedStrong e( 
		new Event_GameObject_Created( ref ) );
	Messenger::GetInstance().Bang( e );

	// adding default components
	ref->AddComponent<Boon::Transform>();
	return ref;
}

Boon::GameObjectWeak Boon::GameObject::GetByID( const GameObjectID& id )
{
	auto result = m_GameObjects.find( id );
	if( result != m_GameObjects.end() )
		return result->second;
	result = m_NewCreated.find( id );
	if( result != m_NewCreated.end() )
		return result->second;
	return GameObjectWeak();
}

void Boon::GameObject::SetComponentEnabled( bool flag, GameComponent* c )
{
	SetComponentEnabled( flag, c->GetComponentID() );
}

void Boon::GameObject::SetComponentEnabled( bool flag, const ComponentID id )
{
	// only attached components
	GameComponent* c = GetComponent( id ).Lock().Get();
	if( c )
	{
		// Only enabling when disabled
		if( flag && !c->IsEnabled() )
		{
			c->OnEnable();
			m_UpdatedComponents.push_back( *c );
		}
		// Only disabling when enabled
		else if( !flag && c->IsEnabled() )
		{
			c->OnDisable();
			m_UpdatedComponents.remove( *c );
		}
	}
}

bool Boon::GameObject::ProcessDirty()
{
	// Call on Startup for all previously new created GameComponents
	auto iter = m_DirtyComponents.begin();
	auto end = m_DirtyComponents.end();

	for( ; iter != end; )
	{
		GameComponent& c = **iter;
		auto prevI = iter;
		++iter; // advance because of possible removal
		if( c.IsEnabled() ) // OnStartup only on enabled components
		{
			c.OnStart();
			m_DirtyComponents.erase( prevI );
		}
	}

	// only remove gameobject from dirty list if all components got processed
	if( m_DirtyComponents.size() )
		return false;
	return true;
}

void Boon::GameObject::Update()
{
	auto iter = m_UpdatedComponents.begin();
	auto end = m_UpdatedComponents.end();
	for( ; iter != end; ++iter )
		iter->Update();
}

void Boon::GameObject::LateUpdate()
{
	auto iter = m_UpdatedComponents.begin();
	auto end = m_UpdatedComponents.end();
	for( ; iter != end; ++iter )
		iter->LateUpdate();
}

Weak_ptr<GameComponent> Boon::GameObject::GetComponent( const ComponentID id )
{
	auto result = m_ComponentsPerCompID.find( id );
	if( result != m_ComponentsPerCompID.end() )
		return result->second;
	return Weak_ptr<GameComponent>();
}

void Boon::GameObject::Destroy()
{
	ProcessManager::GetInstance().DestroyGameObject( GetUniqueObjectID() );
}

void Boon::GameObject::Destroy( const ComponentID id )
{
	auto result = m_ComponentsPerCompID.find( id );
	if( result != m_ComponentsPerCompID.end() )
	{
		GameComponent* comp = result->second.Get();
		if( comp->m_Enabled )
		{
			m_UpdatedComponents.remove( *comp );
			comp->OnDisable();
		}
		comp->OnDestroy();
		m_ComponentsPerCompID.erase( id );
	}
}

Transform& Boon::GameObject::GetTransform()
{
	if( !m_Transform )
		m_Transform = GetComponent<Boon::Transform>().Lock().Get(); //safe, no go without transform
	return *m_Transform;
}

// cache important components
Boon::Renderer* Boon::GameObject::GetRenderer()
{
	if( !m_Renderer )
	{
		RendererWeak weak = GetComponentBase<Boon::Renderer>();
		if( !weak.Empty() )
			m_Renderer = &weak.Lock();
	}
	return m_Renderer;
}

Boon::Collider* Boon::GameObject::GetCollider()
{
	if( !m_Collider )
	{
		ColliderWeak weak = GetComponentBase<Boon::Collider>();
		if( !weak.Empty() )
			m_Collider = &weak.Lock();
	}
	return m_Collider;
}

Boon::Rigidbody* Boon::GameObject::GetRigidbody()
{
	if( !m_Rigidbody )
	{
		RigidbodyWeak weak = GetComponentBase<Boon::Rigidbody>();
		if( !weak.Empty() )
			m_Rigidbody = &weak.Lock();
	}
	return m_Rigidbody;
}



